package org.lgiavedoni.android.franq.service.phone.location;

import java.util.ArrayList;
import java.util.List;

import org.lgiavedoni.android.franq.core.position.Position;
import org.lgiavedoni.android.franq.core.position.PositionManager;
import org.lgiavedoni.android.franq.core.service.Service;
import org.lgiavedoni.android.franq.service.config.ConfigService;
import org.lgiavedoni.android.franq.service.config.ConfigService.ConfigKey;
import org.lgiavedoni.android.franq.service.log.Logger;
import org.lgiavedoni.android.franq.service.phone.ContextService;
import org.lgiavedoni.android.franq.service.phone.location.updatelocation.UpdateLocationStrategy;
import org.lgiavedoni.android.franq.util.LoopUtil;
import org.lgiavedoni.android.franq.util.date.DateUtil;
import org.lgiavedoni.android.franq.util.thread.ThreadUtil;

import android.content.Context;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.telephony.TelephonyManager;
import android.telephony.gsm.GsmCellLocation;

public class LocationService extends Service {

	public static final LocationService inst = new LocationService();
	private LocationManager locManager = null;
	private final LocationListener locListener = new org.lgiavedoni.android.franq.service.phone.location.LocationListener();
	// private static final String IMPLEMENTATIONS_KEY = "locationStrategies";
	private final List<UpdateLocationStrategy> strategies = new ArrayList<UpdateLocationStrategy>();
	private TelephonyManager telephonyManager = null;

	private LocationService() {
		for (Object obj : getImplementations(UpdateLocationStrategy.class)) {
			strategies.add((UpdateLocationStrategy) obj);
		}
		if (ContextService.inst.isApplicationContextLoaded()) {
			telephonyManager = (TelephonyManager) ContextService.inst.getApplicationContext().getSystemService(Context.TELEPHONY_SERVICE);
		}
	}

	public Position getLocation() {
		if (isStopped()) {
			return null;
		}

		// Check for standard application
		// TODO move to a mock class
		if (!ContextService.inst.isApplicationContextLoaded()) {
			return Position.getDefault();
		}

		// FIXME just for now. quiero probar con el cellId
		// start listener
		// startLocationListener();

		long time = DateUtil.getTimeInMillis();
		// wait for location
		ThreadUtil.sleep(1000);
		Position position = _getLocation();

		while (position == null && (DateUtil.getTimeInMillis() < (time + ConfigService.inst.getValueAsLong(ConfigKey.LOCATION_SERVICE_TIME_OUT)))) {
			ThreadUtil.sleep(10000);
			position = _getLocation();
		}

		// FIXME just for now. quiero probar con el cellId
		// stopLocationListener();
		return position;
	}

	private Position _getLocation() {
		GsmCellLocation locationGSM = (GsmCellLocation) telephonyManager.getCellLocation();
		if (isValidLocation(locationGSM)) {
			return Position.createInstanceFrom(locationGSM);
		}

		Location location = null;
		// GPS
		if (locManager.isProviderEnabled(LocationManager.GPS_PROVIDER))
			location = locManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);

		if (!isValidLocation(location)) {
			// NETWORK_PROVIDER
			if (locManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER))
				location = locManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
		}

		// Check if is valid date
		if (!isValidLocation(location)) {
			if (location != null)
				Logger.inst.debug("Location [" + location.getLatitude() + "/" + location.getLongitude() + "] discarded. Current time ["
						+ DateUtil.parseDateTime(DateUtil.getTimeInMillis()) + "] location time [" + DateUtil.parseDateTime(location.getTime()) + "]");
			// The location is to old
			location = null;
		}

		return location != null ? Position.createInstanceFrom(location) : null;
	}

	private boolean isValidLocation(Location location) {

		long validTime = DateUtil.getTimeInMillis() - ConfigService.inst.getValueAsLong(ConfigKey.LOCATION_VALID_TIME);

		if (location != null && (location.getTime() >= validTime))
			return true;
		return false;
	}

	private boolean isValidLocation(GsmCellLocation location) {
		return location.getCid() != -1 ? true : false;
	}

	private void startLocationListener() {
		LoopUtil.prepare();
		if (locManager != null) {
			locManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, locListener);
			locManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, locListener);
		}
	}

	private void stopLocationListener() {
		locManager.removeUpdates(locListener);
	}

	@Override
	protected void startInternal() {
		for (UpdateLocationStrategy s : strategies) {
			s.applayStrategy();
		}
		// Try to update the position
		PositionManager.inst.tryToUpdatePosition();
	}

	@Override
	public void _run() {
		LoopUtil.prepare();
		if (locManager == null && ContextService.inst.isApplicationContextLoaded()) {
			locManager = (LocationManager) ContextService.inst.getApplicationContext().getSystemService(Context.LOCATION_SERVICE);
		}
	}

	@Override
	public String getServiceName() {
		return "location";
	}

	@Override
	public void stopInternal() {
		if (locManager != null) {
			// locManager.removeUpdates(locListener);
			locManager = null;
		}
		for (UpdateLocationStrategy s : strategies) {
			s.cancelStrategy();
		}

	}

}
